﻿#region

using System;
using System.Collections;
using System.Collections.Generic;

#endregion

namespace GGEditor.API.Utility.Collections
{
    public interface IListIndexer<T> : IEnumerable<T>
    {
        int Count { get; }
        T this[int i] { get; }
    }

    public interface IListEditor<T> : IListIndexer<T>
    {
        new T this[int i] { get; set; }
    }

    public class ListIndexer<T> : IListIndexer<T>
    {
        private IList<T> list;

        public ListIndexer(IList<T> list)
        {
            this.list = Contract.NotNull(list, "list");
        }

        #region IListIndexer<T> Members

        public int Count
        {
            get
            {
                lock (this)
                    return list.Count;
            }
        }

        public T this[int i]
        {
            get
            {
                lock (this) {
                    Contract.IsInRange(i, 0, Count, "i");
                    return list[i];
                }
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            lock (this) {
                for (int i = 0; i < Count; i++)
                    yield return this[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    public class ListIndexer<U, V> : IListIndexer<V> where U : class where V : class
    {
        private IList<U> list;

        public ListIndexer(IList<U> list)
        {
            this.list = Contract.NotNull(list, "list");
        }

        #region IListIndexer<V> Members

        public int Count
        {
            get
            {
                lock (this)
                    return list.Count;
            }
        }

        public V this[int i]
        {
            get
            {
                lock (this) {
                    Contract.IsInRange(i, 0, Count, "i");
                    return list[i] as V;
                }
            }
        }

        public IEnumerator<V> GetEnumerator()
        {
            lock (this) {
                for (int i = 0; i < Count; i++)
                    yield return this[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    public class DelegateListIndexer<T> : IListEditor<T>, IListIndexer<T>
    {
        #region Delegates

        public delegate int Counter();

        public delegate T Getter(int i);

        public delegate void Setter(int i, T value);

        #endregion

        private readonly Counter counter;

        private readonly Getter getter;
        private readonly Setter setter;

        public DelegateListIndexer(Counter counter, Getter getter, Setter setter)
        {
            this.counter = Contract.NotNull(counter, "counter");
            this.getter = Contract.NotNull(getter, "getter");
            this.setter = Contract.NotNull(setter, "setter");
        }

        public DelegateListIndexer(Counter counter, Getter getter)
        {
            this.counter = Contract.NotNull(counter, "counter");
            this.getter = Contract.NotNull(getter, "getter");
            setter =
                delegate { throw new InvalidOperationException("This DelegateListIndexer<T> is read-only."); };
        }

        #region IListEditor<T> Members

        public int Count
        {
            get { return counter(); }
        }

        T IListIndexer<T>.this[int i]
        {
            get { return this[i]; }
        }

        public T this[int i]
        {
            get
            {
                Contract.IsInRange(i, 0, Count, "i");
                return getter(i);
            }
            set
            {
                Contract.IsInRange(i, 0, Count, "i");
                setter(i, value);
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < Count; i++)
                yield return this[i];
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }
}